#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

//为具体的泛型类型实现方法 ()
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

#[cfg(test)]
mod const_test {
    // [i32; 2] 和 [i32; 3] 是不同的数组类型

    use crate::basex::generics::generic::Point;

    // 引用解决
    fn display_array<T: std::fmt::Debug>(arr: &[T]) {
        println!("{:?}", arr);
    }

    // where 子句约束
    fn display_array_2<T>(arr: &[T])
    where
        T: std::fmt::Debug,
    {
        println!("{:?}", arr);
    }

    #[test]
    fn main() {
        let p2 = Point {
            x: "Hello",
            y: "da",
        };

        println!("{:?}", p2);
        // p2.distance_from_origin(); //err str

        let p1 = Point { x: 5.11, y: 10.11 };
        p1.distance_from_origin(); //ok

        let arr2: [i32; 2] = [1, 3];
        let arr3: [i32; 3] = [1, 2, 3];
        display_array(&arr2);
        display_array(&arr3);
    }
}

#[cfg(test)]
mod const_generic {
    /*

    常量参数可以在任何可以使用常量项的地方使用，但在类型或数组定义中的重复表达式中使用时，必须如下所述是独立的。也就是说，它们可以在以下地方上允许：

            可以用于类型内部，用它来构成所涉及的程序项签名的一部分。
            作为常量表达式的一部分，用于定义关联常量项，或作为关联类型的形参。
            作为程序项里的任何函数体中的任何运行时表达式中的值。
            作为程序项中任何函数体中使用到的任何类型的参数。
            作为程序项中任何字段类型的一部分使用。
    */

    // 用作结构体的字段
    struct Foo<const N: usize>([i32; N]);

    impl<const N: usize> Foo<N> {
        // 用作关联常数
        const CONST: usize = N * 4;
    }

    trait Trait {
        type Output;
    }

    impl<const N: usize> Trait for Foo<N> {
        // 用作关联类型
        type Output = [i32; N];
    }

    // 不能使用常量泛型参数的示例
    fn foo<const N: usize>() {
        // 能在函数体中的程序项定义中使用
        // const BAD_CONST: [usize; N] = [1; N];
        // static BAD_STATIC: [usize; N] = [1; N];
        // fn inner(bad_arg: [usize; N]) {
        //     let bad_value = N * 2;
        // }
        // type BadAlias = [usize; N];
        // struct BadStruct([usize; N]);
    }

    #[test]
    fn main() {
        let arr2: [i32; 2] = [1, 3];
        let arr3: [i32; 3] = [1, 2, 3];
        // const泛型 针对值的泛型，正好可以用于处理数组长度
        display_array_const(arr2);
        display_array_const(arr3);
    }

    //重点在于 N 这个泛型参数，它是一个基于值的泛型常量 泛型参数！因为它用来替代的是数组的长度。
    fn display_array_const<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {
        println!("{:?}", arr);
    }

    // 常量只能作为类型或数组定义中的重复表达式中的独立实参出现
    // 它们只能以单段路径表达式的形式使用（例如 N 或以块{N} 的形式出现）。也就是说，它们不能与其他表达式结合使用。
    /*
      不允许在类型中的表达式中组合使用，例如这里的返回类型中的算术表达式
      fn bad_function<const N: usize>() -> [u8; {N + 1}] {
        // 同样的，这种情况也不允许在数组定义里的重复表达式中使用
        [1; {N + 1}]
    }
    路径中的常量实参指定了该程序项使用的常量值。实参必须是常量形参所属类型的常量表达式。
    常量表达式必须是块表达式（用花括号括起来），除非它是单独路径段（一个[标识符][IDENTIFIER]）或一个字面量
     */

    fn double<const N: i32>() {
        println!("doubled: {}", N * 2);
    }

    const SOME_CONST: i32 = 12;

    #[test]
    fn example() {
        // 常量参数的使用示例。
        double::<9>();
        double::<-123>();
        double::<{ 7 + 8 }>();
        double::<SOME_CONST>();
        double::<{ SOME_CONST + 5 }>();

        type N = u32;
        struct Foo<const N: usize>;
        // 下面用法是错误的，因为 `N` 被解释为类型别名。
        // fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
        // 可以使用花括号来强制将 `N` 解释为常量形参。
        fn bar<const N: usize>() -> Foo<{ N }> {
            todo!()
        } // ok
    }
}

// 与类型参数 和 生存期参数 不同
// 常量参数可以声明而不必在被它参数化的程序项中使用，
// 但 和泛型实现关联 的 实现例外

// ok
struct Foo<const N: usize>;

enum Bar<const M: usize> {
    A,
    B,
}

// ERROR: 参数未使用
// struct Baz<T>; // parameter `T` is never used
// struct Biz<'a>; // parameter `'a` is never used
struct Unconstrained;
// impl<const N: usize> Unconstrained {} //err

#[cfg(test)]
mod const_match {
    struct Foo<const B: bool>;
    trait Bar {}
    impl Bar for Foo<true> {}
    impl Bar for Foo<false> {}

    fn needs_bar(_: impl Bar) {}
    // 当处理 trait约束时，在确定是否满足相关约束时，不会考虑常量参数的所有实现的穷尽性
    fn generic<const B: bool>() {
        let v = Foo::<B>;
        // needs_bar(v); // ERROR: trait约束 `Foo<B>: Bar` 未被满足
    }
}
